home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-09-28 | 36.2 KB | 1,388 lines |
- /*
- * Program: Distributed Electronic Mail Manager (MBoxWindow object)
- *
- * Author: Mark Crispin
- * Networks and Distributed Computing
- * Computing & Communications
- * University of Washington
- * Administration Building, AG-44
- * Seattle, WA 98195
- * Internet: MRC@CAC.Washington.EDU
- *
- * Date: 23 February 1989
- * Last Edited: 28 September 1992
- *
- * Copyright 1992 by the University of Washington
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose and without fee is hereby granted, provided
- * that the above copyright notice appears in all copies and that both the
- * above copyright notice and this permission notice appear in supporting
- * documentation, and that the name of the University of Washington not be
- * used in advertising or publicity pertaining to distribution of the software
- * without specific, written prior permission. This software is made
- * available "as is", and
- * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
- * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
- * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
- * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
- * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
- * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
-
-
- #import "MailManager.h"
-
- @implementation MBoxWindow
-
- // Create a new MBoxWindow
-
- + new
- {
- self = [super new]; // create ourselves
- // get the interface
- [NXApp loadNibSection:"MBoxWindow.nib" owner:self withNames:NIL];
- mailStream = NIL; // no stream yet
- entryID = NIL; // no entry ID either
- nmsgs = 0; // initialize number of messages
- keywordPanel = selectPanel = NIL;
- return self; // give us back to caller
- }
-
-
- // Note our window
-
- - setWindow:anObject
- {
- // note the window's frame
- [(window = anObject) getFrame:&windowFrame];
- [window setDelegate:self]; // we're the window's delegate
- return self;
- }
-
- // Create the message items view
-
- - setMessageItems:anObject
- {
- NXSize size;
- NXRect frame;
- [anObject getFrame:&frame]; // get frame of our message items view
- // create a scroll view
- [(messageItems = [ScrollView newFrame:&frame]) setBorderType:NX_BEZEL];
- // only vertical scrolling is required
- [messageItems setVertScrollerRequired:T];
- // allow its size to change
- [messageItems setAutosizing:NX_HEIGHTSIZABLE|NX_WIDTHSIZABLE];
- // put up the view
- [[anObject superview] replaceSubview:anObject with:messageItems];
- // get the size minus the scroll bars
- [messageItems getContentSize:&size];
- // set it as such in the browser frames
- NX_WIDTH (&frame) = size.width; NX_HEIGHT (&frame) = size.height;
- // create the browser
- browser = [Matrix newFrame:&frame mode:NX_LISTMODE
- cellClass:[BrowserCell class] numRows:0 numCols:1];
- // create the zoomer
- zoomer = [Matrix newFrame:&frame mode:NX_LISTMODE
- cellClass:[BrowserCell class] numRows:0 numCols:1];
- size.height = 13.0; // just tall enough to hold the text
- [browser setCellSize:&size]; // set the size to the minimum
- [zoomer setCellSize:&size]; // set the zoomer size to the minimum
- // no intercell spacing
- size.width = 0; size.height = 0;
- [browser setIntercell:&size];
- [zoomer setIntercell:&size];
- [browser sizeToCells]; // resize the browser
- [zoomer sizeToCells]; // resize the zoomer
- [browser setAutoscroll:T]; // make it autoscrolling
- [zoomer setAutoscroll:T]; // make it autoscrolling
- [browser setTarget:self]; // make double-click read that message
- [zoomer setTarget:self]; // make zoomer double-click read that message
- if (lockedread) { // single click reads message if locked
- [browser setAction:@selector(readMessage:)];
- [zoomer setAction:@selector(readMessage:)];
- }
- else { // else use double click
- [browser setDoubleAction:@selector(readMessage:)];
- [zoomer setDoubleAction:@selector(readMessage:)];
- }
- // make the browser the scroller's docView
- [messageItems setDocView:browser];
- [messageItems display]; // put up the whole browser
- return self;
- }
-
- // Non-interface methods
-
-
- // Set MAIL stream for window
-
- - setStream:(MAILSTREAM *)stream
- {
- char tmp[TMPLEN];
- int m = -1;
- // in case server didn't give us an EXISTS
- int n = (getstreamprop (stream)->nmsgs);
- // note the stream and its MBoxWindow
- putstreamprop ((mailStream = stream),self);
- [self exists:n]; // note number of messages from streamprop
- // only do this if can do selections
- if (!lockedread && stream->mailbox) {
- strcpy (tmp,"UNSEEN"); // do search (note: criteria must be writable)
- mail_search (mailStream,tmp);
- [self zoom:self]; // do zoom action
- }
- if (![zooming intValue]) { // only if not zooming
- // get browser range and resize
- [[browser getNumRows:&n numCols:&m] sizeToCells];
- // look for a selected message
- for (m = 0; m < n && ![[browser cellAt:m :0] state]; ++m);
- // make sure first recent message visible
- [browser scrollCellToVisible:min (n-1,m+19) :0];
- }
- if (*stream->mailbox == '{' || (*stream->mailbox == '*' &&
- stream->mailbox[1] == '{')) {
- mail_find (stream,"*"); // learn about more mailboxes and BBoards
- if (readbboards) mail_find_bboards (stream,"*");
- }
- if (interval) // set up periodic event to wake us up
- entryID = DPSAddTimedEntry (interval,(DPSTimedEntryProc) mm_wakeup,
- (void *) self,5);
- // want automatic message reading?
- if (autoread) [self readMessage:self];
- return self;
- }
-
- // Update the title and display of the message selection window, pop cursor
-
- - updateTitle
- {
- char tmp[TMPLEN];
- if (mailStream && mailStream->mailbox) {
- sprintf (tmp,"%s%s -- %lu Messages, %lu Recent",
- [zooming intValue] ? "Zoomed " : "",mailStream->mailbox,
- mailStream->nmsgs,mailStream->recent);
- [browser sizeToCells]; // resize the browser to flush it
- [messageItems display]; // redisplay the beast
- }
- else strcpy (tmp,"<No mailbox open>");
- [window setTitle:tmp]; // set window's title
- return self;
- }
-
-
- // Return our window
-
- - window
- {
- return window; // return our window
- }
-
- // Recalculate sequence
-
- - (int) updateSequence
- {
- int f = 0;
- int m,n;
- int msgno,range;
- int beg = 0;
- id browse = [zooming intValue] ? zoomer : browser;
- id cell;
- char *seq = sequence;
- *seq = '\0'; // destroy old sequence poop
- // get range of this browser
- [browse getNumRows:&n numCols:&m];
- // for each selected message
- for (m = (range = 0); m < n; ++m) {
- if ([(cell = [browse cellAt:m :0]) state]) {
- msgno = [cell msgno]; // get message number for this cell
- if (range) { // range in progress?
- // yes, can we continue it?
- if (msgno == range+1) range = msgno;
- else { // can't, close off the range if appropriate
- if (range != beg) sprintf (seq,":%d,%d",range,msgno);
- else sprintf (seq,",%d",msgno);
- range = (beg = msgno);// start a new range
- }
- }
- else { // output this sequence
- if (f) *seq++ = ','; // start with a comma if not first time
- else f = T; // first time only once
- sprintf (seq,"%d",msgno);
- range = (beg = msgno); // start a new range
- }
- }
- else { // close off the range if there was one
- if (range && (range != beg)) sprintf (seq,":%d",range);
- range = 0; // no longer in a range
- }
- seq += strlen (seq); // update the pointer (broken sprintf...)
- }
- // close off the range if still one
- if (range && (range != beg)) sprintf (seq,":%d",range);
- return (f); // return flag to caller
- }
-
- // Methods invoked by MAIL library via MailManager/streamprops
-
-
- // Message exists (i.e. there are that many messages in the mailbox)
-
- - exists:(int) msgno;
- {
- char tmp[TMPLEN];
- char *word;
- int i = msgno - nmsgs;
- if (nmsgs) word = " new "; // if not first time
- else word = " "; // first time not "new"
- if (i < 0) { // this is very very bad
- sprintf (tmp,"Bug: Mailbox shrunk from %d to %d",nmsgs,msgno);
- fatal (tmp);
- }
- switch (i) { // based on the number
- case 0: // no output if zero
- return self;
- case 1: // let's have halfway decent English here...
- sprintf (tmp,"There is 1%smessage in %s",word,mailStream->mailbox);
- break;
- default:
- sprintf (tmp,"There are %d%smessages in %s",i,word,mailStream->mailbox);
- break;
- }
- mm_log (tmp,NIL); // output the string
- if (nmsgs) NXBeep (); // beep if a new message
- // We don't have to worry about locking the elt or getting a handle on
- // the stream, since there is no way that this cell can survive the death
- // of either. The stream can only go away if this window goes away, and
- // the elt can only go away through the expunged: method below.
- for (; i--; nmsgs++) [[[browser addRow] cellAt:nmsgs :0] setStream:mailStream
- element:mail_elt (mailStream,nmsgs+1)];
- // Although this would be nice, it can cause mail_fetchstructure () to be
- // called, which is a no-no since the stream is locked.
- //[browser sizeToCells]; // resize the browser to hold them
- return self;
- }
-
- // Message matches a search
-
- - searched:(int) msgno;
- {
- int i = msgno - 1;
- [browser setState:T at:i :0]; // set its state to true
- return self;
- }
-
-
- // Message expunged
-
- - expunged:(int) msgno;
- {
- // flush the message from the browser
- [browser removeRowAt:msgno-1 andFree:T];
- // Although this is neat to watch, it's intolerably slow when you're trying to
- // get real work done. What's more, it might cause mail_fetchstructure () to
- // be called, which is a no-no since the stream is locked. Consequently, the
- // expunge: method does this, once after all the expunges are done
- //[browser sizeToCells]; // resize the browser to flush it
- nmsgs--; // decrement number of messages
- return self;
- }
-
- // Bottom row buttons
-
-
- // Check button - check for new messages
-
- - check:sender
- {
- mail_check (mailStream); // check for new messages
- return [self updateTitle]; // update title, pop cursor, return
- }
-
-
- // No-op - ping connection to see if alive
-
- - noop:sender
- {
- if (mailStream && !mailStream->lock) {
- // update title, pop cursor
- if (mail_ping (mailStream)) [self updateTitle];
- else {
- [self close]; // do close action
- mail_close (mailStream); // nuke the stream and associated streamprop
- putstreamprop (mailStream,NIL);
- mailStream = NIL; // drop pointer to deceased stream
- if (lockedopen) [NXApp close];
- }
- }
- return self;
- }
-
-
- // Exit button - Expunge and close window
-
- - exit:sender
- {
- // expunge the mailbox unless will autoexpunge
- if (sender && !autoexpunge) [self expunge:sender];
- [window close]; // close the window
- [self windowWillClose:sender];// do cleanup actions
- return self;
- }
-
-
- // Expunge button - destroy deleted messages
-
- - expunge:sender
- {
- mail_expunge (mailStream); // expunge the mailbox
- return [self zoom:self]; // rezoom if necessary
- }
-
- // Close window
-
- - close
- {
- [selectPanel close]; // sayonara to select panel
- // flush the wakeup call
- if (entryID) DPSRemoveTimedEntry (entryID);
- entryID = NIL; // drop pointer to it
- // nuke a locked read window too
- if (lockedread) [[reader setFreeWhenClosed:T] close];
- reader = NIL; // drop its pointer
- [window close]; // close the window
- return self;
- }
-
-
- // Window is closing
-
- - windowWillClose:sender
- {
- window = NIL; // this window is dying
- [self close]; // do close action
- if (mailStream) { // if still a stream
- // do autoexpunge if appropriate
- if (autoexpunge &&
- NXRunAlertPanel ("Expunge?","Destroy deleted messages?",NIL,"No",NIL))
- mail_expunge (mailStream);
- mail_close (mailStream); // close the MAIL stream
- // and drop the MBoxWindow streamprop
- putstreamprop (mailStream,NIL);
- mailStream = NIL; // drop pointer to deceased stream
- }
- if (lockedopen) [NXApp close];// close the mailbox
- return self;
- }
-
- // Window moved
-
- - windowDidMove:sender
- {
- // make select panel track this window
- [selectPanel attachWindow:window];
- return self;
- }
-
-
- // Window is resizing
-
- - windowWillResize:sender toSize:(NXSize *) frameSize
- {
- // constrain width to maximum
- frameSize->width = min (frameSize->width,NX_WIDTH (&windowFrame));
- return self;
- }
-
- // Mailbox switches
-
-
- // Note our debugging switch
-
- - setDebugging:anObject
- {
- debugging = anObject; // note our debug switch
- [debugging setIntValue:debug];// set its initial value
- return self;
- }
-
-
- // Debug switch - set the protocol telemetry status
-
- - debug:sender
- {
- // do debug or no debug
- if ([debugging intValue]) mail_debug (mailStream);
- else mail_nodebug (mailStream);
- return self;
- }
-
-
- // Note our zooming switch
-
- - setZooming:anObject
- {
- zooming = anObject; // note our zoom switch
- // set its initial value
- [zooming setIntValue:autozoom];
- return self;
- }
-
- // Zoom switch - zoom in on selected messages
-
- - zoom:sender
- {
- int i,j;
- if (mailStream) { // paranoid
- // get the current size of the zoomer
- [zoomer getNumRows:&i numCols:&j];
- // flush the old zoomer rows
- while (i--) [zoomer removeRowAt:i andFree:T];
- if ([zooming intValue]) { // zooming in?
- // show the zoomer
- if ([messageItems docView] != zoomer) [messageItems setDocView:zoomer];
- [zoomer lockFocus]; // lock it on it
- // look for selected messages
- for (i = (j = 0); i < nmsgs; ++i) if ([[browser cellAt:i :0] state]) {
- [zoomer addRow]; // add a row to the zoomer
- // stuff it with the data
- [[zoomer cellAt:j :0] setStream:mailStream
- element:mail_elt (mailStream,i+1)];
- // set its state
- [zoomer setState:T at:j :0];
- // and highlight it
- if (!lockedread) [zoomer highlightCellAt:j++ :0 lit:T];
- }
- [zoomer unlockFocus];
- [zoomer sizeToCells]; // resize the zoomer
- }
- else { // zooming out, restore the browser
- if ([messageItems docView] != browser) [messageItems setDocView:browser];
- if (!lockedread) {
- // get the size of the browser
- [browser getNumRows:&i numCols:&j];
- [browser lockFocus];
- // fix the highlighting in the browser
- for (i = 0; i <nmsgs; ++i)
- [browser highlightCellAt:i :0 lit:[[browser cellAt:i :0] state]];
- [browser unlockFocus];
- }
- }
- }
- return [self updateTitle]; // update title, pop cursor, return
- }
-
- // Keyword management
-
-
- // Note our keyword panel
-
- - setKeywordPanel:anObject
- {
- keywordPanel = anObject; // note our keyword panel
- return self;
- }
-
-
- // Note our keyword scroll view
-
- - setKeywordItems:anObject
- {
- NXRect frame;
- NXSize size;
- int m = -1;
- id cell = [[ButtonCell newTextCell:"keyword"] setAlignment:NX_LEFTALIGNED];
- [anObject getFrame:&frame]; // get frame of our keywords view
- // create a scroll view
- [keywordItems = [ScrollView newFrame:&frame] setBorderType:NX_BEZEL];
- // only vertical scrolling is required
- [keywordItems setVertScrollerRequired:T];
- // make it the view
- [[anObject superview] replaceSubview:anObject with:keywordItems];
- // get the size minus the scroll bars
- [keywordItems getContentSize:&size];
- // set it as such in the keyword browser frame
- NX_WIDTH (&frame) = size.width; NX_HEIGHT (&frame) = size.height;
- // get frame of keyword items view
- keywords = [Matrix newFrame:&frame mode:NX_LISTMODE prototype:cell
- numRows:0 numCols:1];
- [cell calcCellSize:&size]; // get the minimum cell size
- // make cells fill the width of the browser
- size.width = NX_WIDTH (&frame);
- [keywords setCellSize:&size]; // set the cell size
- // no intercell spacing
- size.width = 0; size.height = 0;
- [keywords setIntercell:&size];
- // add user keywords
- if (mailStream) while (mailStream->user_flags[++m])
- [[keywords addRow] setTitle:mailStream->user_flags[m] at:m :0];
- [[keywords addRow] setTitle:"\\Seen" at:m :0];
- [[keywords addRow] setTitle:"\\Answered" at:++m :0];
- [keywords sizeToCells]; // resize the keyword browser to fit
- [keywords setAutoscroll:T]; // make it autoscrolling
- [keywords setTarget:self]; // make double-click set that keyword
- [keywords setDoubleAction:@selector(keywordOK:)];
- // make the browser the scroller's docView
- [keywordItems setDocView:keywords];
- [keywordItems display]; // put up the items
- return self;
- }
-
- // Get keywords
-
- - (char *) getKeywords
- {
- char tmp[TMPLEN];
- int rows,cols;
- id cell;
- if (!keywordPanel) // create new keyword panel if none yet
- [NXApp loadNibSection:"KeywordPanel.nib" owner:self withNames:NIL];
- // put up the keyword panel
- [NXApp runModalFor:keywordPanel];
- if (!OK) return NIL; // punt if user said so
- tmp[0] = '\0'; // make sure string is empty
- // get number of keywords
- [keywords getNumRows:&rows numCols:&cols];
- while (rows) { // sniff through browser
- // if found a selected keyword
- if ([cell = [keywords cellAt:--rows :0] state]) {
- // if not first time delimit with space
- if (tmp[0]) strcat (tmp," ");
- else strcat (tmp,"("); // otherwise open a list
- // now add the keyword
- strcat (tmp,[cell title]);
- }
- }
- if (!tmp[0]) return NIL; // did the user give at least one keyword?
- strcat (tmp,")"); // close the list
- return (cpystr (tmp)); // return keyword list
- }
-
-
- // Cancel keyword changing
-
- - keywordCancel:sender
- {
- [NXApp stopModal]; // end the modality
- [keywordPanel close]; // close the keyword panel
- OK = NIL; // not OK
- return self;
- }
-
-
- // User wants to change keywords
-
- - keywordOK:sender
- {
- [NXApp stopModal]; // end the modality
- [keywordPanel close]; // close the keyword panel
- OK = T; // not OK
- return self;
- }
-
- // Note our Keyword button
-
- - setKeyword:anObject
- {
- id l = [PullOutMenu new:(keyword = anObject)];
- [l addItem:"Keyword..." action:@selector(keywordSet:)];
- [l addItem:"Clear Keyword..." action:@selector(keywordClear:)];
- return self;
- }
-
-
- // Set the selected keywords in selected messages
-
- - keywordSet:sender
- {
- return [self keyword:sender set:T];
- }
-
-
- // Clear the selected keywords in selected messages
-
- - keywordClear:sender
- {
- return [self keyword:sender set:NIL];
- }
-
-
- // Change keywords
-
- - keyword:sender set:(BOOL) set
- {
- char *keys;
- // make sure have valid sequence
- if (mailStream && [self updateSequence] && (keys = [self getKeywords])) {
- // set the flag, if we can
- if (set) mail_setflag (mailStream,sequence,keys);
- else mail_clearflag (mailStream,sequence,keys);
- fs_give ((void **) &keys);
- }
- return [self updateTitle]; // update title, pop cursor, return
- }
-
- // File copying
-
-
- // Note our Copy button
-
- - setCopy:anObject
- {
- id l = [PullOutMenu new:(copy = anObject)];
- [l addItem:"Copy..." action:@selector(fileCopy:)];
- [l addItem:"Move..." action:@selector(fileMove:)];
- return self;
- }
-
-
- // Copy selected messages to another mailbox
-
- - fileCopy:sender
- {
- return [self copy:sender delete:NIL];
- }
-
-
- // Move (copy + delete) selected messages to another mailbox
-
- - fileMove:sender
- {
- return [self copy:sender delete:T];
- }
-
- // Copy selected messages to another mailbox
-
- - copy:sender delete:(BOOL) del
- {
- char *dest;
- int m,n,i;
- FILE *file;
- ENVELOPE *env;
- BODY *body;
- unsigned char *text;
- id cell;
- id browse = [zooming intValue] ? zoomer : browser;
- // get file
- if (mailStream && [self updateSequence] && (dest = [NXApp getFile])) {
- if (*dest == '*') { // want local copy?
- // yes, open local file
- if (!(file = fopen (dest+1,"a")))
- NXRunAlertPanel ("Open failed","Can't open local file",NIL,NIL,NIL);
- else { // get range of this browser
- [browse getNumRows:&n numCols:&m];
- // for each selected message in browser
- for (m = 0; m < n; ++m) if ([(cell = [browse cellAt:m :0]) state]) {
- // get envelope (must be separate instruction!)
- env = mail_fetchstructure (mailStream,(i = [cell msgno]),&body);
- text = (unsigned char *) cpystr (mail_fetchtext (mailStream,i));
- // blat it to the file
- append_msg (file,env ? env->sender : NIL,mail_elt (mailStream,i),
- mail_fetchheader (mailStream,i),text);
- fs_give ((void **) &text);
- }
- fclose (file); // close off the file
- // if move, delete the messages now
- if (del) [self delete:self];
- }
- }
- else { // want remote copy
- if (del) mail_move (mailStream,sequence,dest+1);
- else mail_copy (mailStream,sequence,dest+1);
- }
- fs_give ((void **) &dest); // flush the string
- }
- return [self updateTitle]; // update title, pop cursor, return
- }
-
- // Close current mailbox and open a new mailbox
-
- - newMailbox:sender
- {
- // autoexpunge if appropriate
- if (mailStream && autoexpunge &&
- NXRunAlertPanel ("Expunge?","Destroy deleted messages?",NIL,"No",NIL))
- mail_expunge (mailStream);
- [NXApp reOpen:mailStream window:self];
- return self;
- }
-
-
- // Print selected messages
-
- - print:sender
- {
- int i,m,n;
- char *s;
- id cell;
- id browse = [zooming intValue] ? zoomer : browser;
- // get current selected messages
- if (mailStream && [self updateSequence]) {
- [NXApp startPrint]; // reset printer
- // get range of this browser
- [browse getNumRows:&n numCols:&m];
- for (m = 0; m < n; ++m) if ([(cell = [browse cellAt:m :0]) state]) {
- s = fixnl (cpystr (mail_fetchheader (mailStream,(i = [cell msgno]))));
- [NXApp printText:s]; // print header
- fs_give ((void **) &s);
- [NXApp printText:(s = fixnl (cpystr (mail_fetchtext (mailStream,i))))];
- fs_give ((void **) &s);
- }
- [NXApp print]; // print the whole thing
- }
- return [self updateTitle]; // update title, pop cursor, return
- }
-
-
- // Open help panel
-
- - help:sender
- {
- [NXApp mboxHelp:sender];
- return self;
- }
-
- // Message reading
-
-
- // Read selected messages
-
- - readMessage:sender
- {
- int m,n;
- SEQUENCE *new;
- SEQUENCE *seq = NIL;
- SEQUENCE *old = NIL;
- id cell;
- id browse = [messageItems docView];
- // get range of this browser
- [browse getNumRows:&n numCols:&m];
- for (m = 0; m < n; ++m) // look for selected message or last if locked
- if ([(cell = [browse cellAt:m :0]) state] || (lockedread && (m == n-1))) {
- // found one, make a sequence list item
- new = (SEQUENCE *) fs_get (sizeof (SEQUENCE));
- // get cache element for this message
- new->lelt = mail_lelt (mailStream,[cell msgno]);
- new->previous = old; // previous is old sequence
- new->next = NIL; // no next for it yet
- if (old) old->next = new; // if old, tack new on to old's tail
- else seq = new; // otherwise, start a new sequence
- old = new; // this is now the old sequence
- if (!++(old->lelt->elt.lockcount)) fatal ("Elt lock count overflow");
- if (lockedread) break; // no multi-sequence reads if locked
- }
- if (seq) { // only if we got a sequence
- if (!(lockedread && reader))// make new reader unless locked and have one
- [(reader = [ReadWindow new]) setFreeWhenClosed:!lockedread];
- // give it the message handle
- [reader setHandle:(mail_makehandle (mailStream))];
- // and the view of our message items
- [reader setMessageItems:messageItems];
- [reader setSequence:seq]; // set its sequence
- }
- return [self updateTitle]; // update title, pop cursor, return
- }
-
- // Message status management
-
-
- // Note our Delete button
-
- - setDelete:anObject
- {
- id l = [PullOutMenu new:(delete = anObject)];
- [l addItem:"Delete" action:@selector(delete:)];
- [l addItem:"Undelete" action:@selector(undelete:)];
- return self;
- }
-
-
- // Note our Flag button
-
- - setFlag:anObject
- {
- id l = [PullOutMenu new:(flag = anObject)];
- [l addItem:"Flag" action:@selector(flag:)];
- [l addItem:"Unflag" action:@selector(unflag:)];
- return self;
- }
-
- // Delete selected messages
-
- - delete:sender
- {
- // get current selected messages
- if ([self updateSequence]) mail_setflag (mailStream,sequence,"\\Deleted");
- return [self updateTitle]; // update title, pop cursor, return
- }
-
-
- // Flag selected messages
-
- - flag:sender
- {
- // get current selected messages
- if ([self updateSequence]) mail_setflag (mailStream,sequence,"\\Flagged");
- return [self updateTitle]; // update title, pop cursor, return
- }
-
-
- // Undelete selected messages
-
- - undelete:sender
- {
- // get current selected messages
- if ([self updateSequence]) mail_clearflag (mailStream,sequence,"\\Deleted");
- return [self updateTitle]; // update title, pop cursor, return
- }
-
-
- // Unflag selected messages
-
- - unflag:sender
- {
- // get current selected messages
- if ([self updateSequence]) mail_clearflag (mailStream,sequence,"\\Flagged");
- return [self updateTitle]; // update title, pop cursor, return
- }
-
- // Message forwarding
-
-
- // Note our Forward button
-
- - setForward:anObject
- {
- id l = [PullOutMenu new:(forward = anObject)];
- [l addItem:"Forward..." action:@selector(forward:)];
- [l addItem:"Remail..." action:@selector(remail:)];
- return self;
- }
-
-
- // Remail messages
-
- - remail:sender
- {
- int i,m,n;
- char *s;
- ENVELOPE *msg;
- id cell,compose,body;
- id browse = [zooming intValue] ? zoomer : browser;
- // get range of this browser
- [browse getNumRows:&n numCols:&m];
- // get current selected messages
- if (mailStream && [self updateSequence])
- // compose a remail for each selected message
- for (m = 0; m < n; ++m) if ([(cell = [browse cellAt:m :0]) state]) {
- compose = [NXApp compose:self];
- msg = [compose msg]; // get message structure for it
- // set remail header
- msg->remail = cpystr (mail_fetchheader (mailStream,(i = [cell msgno])));
- body = [compose bodyView];// get the message text body view
- [body selectAll:sender]; // prepare to initialize text
- [body replaceSel:(s = fixnl (cpystr (mail_fetchtext (mailStream,i))))];
- fs_give ((void **) &s);
- [body setSel:0:0]; // put cursor at start of message
- [body setEditable:NIL]; // can't change remail text
- [body display]; // update the display
- [compose selectBody]; // now select the appropriate field
- }
- return [self updateTitle]; // update title, pop cursor, return
- }
-
- // Forward messages
-
- - forward:sender
- {
- char tmp[TMPLEN];
- char *s;
- int i,m,n;
- ENVELOPE *msg;
- ENVELOPE *env;
- BODY *b;
- id cell;
- id body = NIL;
- id compose = NIL;
- id browse = [zooming intValue] ? zoomer : browser;
- // get range of this browser
- [browse getNumRows:&n numCols:&m];
- // get current selected messages
- if (mailStream && [self updateSequence]) {
- // compose a remail for each selected message
- for (m = 0; m < n; ++m) if ([(cell = [browse cellAt:m :0]) state]) {
- env = mail_fetchstructure (mailStream,(i = [cell msgno]),&b);
- if (!compose) { // make a compose window if first time through
- compose = [NXApp compose:self];
- msg = [compose msg]; // get message structure for it
- tmp[0] = '[';
- mail_fetchfrom (tmp+1,mailStream,[cell msgno],FROMLEN);
- // tie off trailing spaces
- for (s = tmp+FROMLEN; *s == ' '; --s) *s = '\0';
- if (env && env->subject) sprintf (s + 1,": %s]",env->subject);
- else strcpy (s + 1,"]");
- msg->subject = cpystr (tmp);
- // update the subject field
- [compose updateEnvelopeView];
- // get the message text body view
- [body = [compose bodyView] selectAll:sender];
- [body replaceSel:"\n ** Begin Forwarded Message(s) **\n\n"];
- }
- // insert message
- [body replaceSel:(s = (literaldisplay || !env) ?
- fixnl (cpystr (mail_fetchheader (mailStream,i))) :
- filtered_header (env))];
- fs_give ((void **) &s);
- [body replaceSel:(s = fixnl (cpystr (mail_fetchtext (mailStream,i))))];
- fs_give ((void **) &s);
- }
- if (compose) { // did we get to compose a message?
- [body setSel:0:0]; // put cursor at start of message
- [body display]; // update the display
- [compose selectBody]; // now select the appropriate field
- }
- }
- return [self updateTitle]; // update title, pop cursor, return
- }
-
- // Message replying
-
-
- // Note our Reply button
-
- - setReply:anObject
- {
- id l = [PullOutMenu new:(reply = anObject)];
- [l addItem:"Reply..." action:@selector(replySender:)];
- [l addItem:"Reply to All..." action:@selector(replyAll:)];
- return self;
- }
-
-
- // Reply to reply address of selected messages
-
- - reply:sender all:(BOOL) all
- {
- int i,m,n;
- id cell;
- BODY *body;
- id browse = [zooming intValue] ? zoomer : browser;
- // get range of this browser
- [browse getNumRows:&n numCols:&m];
- // get current selected messages
- if (mailStream && [self updateSequence])
- // compose a reply for each selected message
- for (m = n - 1; m >= 0; --m) if ([(cell = [browse cellAt:m :0]) state]) {
- // make sure cache loaded
- mail_fetchstructure (mailStream,(i = [cell msgno]),&body);
- mail_elt (mailStream,i); // start a reply window
- [ReplyWindow new:mail_makehandle (mailStream)
- lelt:mail_lelt (mailStream,i) text:mail_fetchtext (mailStream,i)
- all:all];
- }
- return [self updateTitle]; // update title, pop cursor, return
- }
-
-
- // Reply to all (reply address + recipients) of selected messages
-
- - replyAll:sender
- {
- return [self reply:sender all:T];
- }
-
-
- // Reply to reply address of selected messages
-
- - replySender:sender
- {
- return [self reply:sender all:NIL];
- }
-
- // Message selection
-
-
- // Note our select panel
-
- - setSelectPanel:anObject
- {
- selectPanel = anObject; // note our select panel
- return self;
- }
-
-
- // Note our After switch
-
- - setSelectAfter:anObject
- {
- selectAfter = anObject; // note our After switch
- return self;
- }
-
-
- // Note our Answered switch
-
- - setSelectAnswered:anObject
- {
- selectAnswered = anObject; // note our Answered switch
- return self;
- }
-
-
- // Note our Before switch
-
- - setSelectBefore:anObject
- {
- selectBefore = anObject; // note our Before switch
- return self;
- }
-
-
- // Note our Cc text
-
- - setSelectCc:anObject
- {
- selectCc = anObject; // note our Cc text
- return self;
- }
-
- // Note our Date2 view
-
- - setSelectDate2:anObject
- {
- selectDate2 = anObject; // note our Date2 view
- // note its superview
- selectDate2Super = [selectDate2 superview];
- // make it invisible
- [selectDate2 removeFromSuperview];
- [selectPanel display];
- return self;
- }
-
-
- // Note our Date text
-
- - setSelectDateText:anObject
- {
- selectDateText = anObject; // note our DateText text
- // blank out the value
- [selectDateText setStringValue:""];
- return self;
- }
-
-
- // Note our DateText2 text
-
- - setSelectDateText2:anObject
- {
- selectDateText2 = anObject; // note our DateText2 text
- // disable the cell
- [selectDateText2 setEnabled:NIL];
- // blank out the value
- [selectDateText2 setStringValue:""];
- return self;
- }
-
- // Note our Deleted switch
-
- - setSelectDeleted:anObject
- {
- selectDeleted = anObject; // note our Deleted switch
- return self;
- }
-
-
- // Note our Flagged switch
-
- - setSelectFlagged:anObject
- {
- selectFlagged = anObject; // note our Flagged switch
- return self;
- }
-
-
- // Note our From text
-
- - setSelectFrom:anObject
- {
- selectFrom = anObject; // note our From text
- return self;
- }
-
-
- // Note our New switch
-
- - setSelectNew:anObject
- {
- selectNew = anObject; // note our New switch
- return self;
- }
-
-
- // Note our Old switch
-
- - setSelectOld:anObject
- {
- selectOld = anObject; // note our Old switch
- return self;
- }
-
-
- // Note our Other text
-
- - setSelectOther:anObject
- {
- selectOther = anObject; // note our Other text
- return self;
- }
-
- // Note our Recent switch
-
- - setSelectRecent:anObject
- {
- selectRecent = anObject; // note our Recent switch
- return self;
- }
-
-
- // Note our Retain switch
-
- - setSelectRetain:anObject
- {
- selectRetain = anObject; // note our Retain switch
- return self;
- }
-
-
- // Note our Seen switch
-
- - setSelectSeen:anObject
- {
- selectSeen = anObject; // note our Seen switch
- return self;
- }
-
-
- // Note our Subject text
-
- - setSelectSubject:anObject
- {
- selectSubject = anObject; // note our Subject text
- return self;
- }
-
-
- // Note our Text text
-
- - setSelectText:anObject
- {
- selectText = anObject; // note our Text text
- return self;
- }
-
-
- // Note our Texts form
-
- - setSelectTexts:anObject
- {
- selectTexts = anObject; // note our Text form
- return self;
- }
-
- // Note our To text
-
- - setSelectTo:anObject
- {
- selectTo = anObject; // note our To text
- return self;
- }
-
-
- // Note our Unanswered switch
-
- - setSelectUnanswered:anObject
- {
- selectUnanswered = anObject; // note our Unanswered switch
- return self;
- }
-
-
- // Note our Undeleted switch
-
- - setSelectUndeleted:anObject
- {
- selectUndeleted = anObject; // note our Undeleted switch
- return self;
- }
-
-
- // Note our Unflagged switch
-
- - setSelectUnflagged:anObject
- {
- selectUnflagged = anObject; // note our Unflagged switch
- return self;
- }
-
-
- // Note our Unseen switch
-
- - setSelectUnseen:anObject
- {
- selectUnseen = anObject; // note our Unseen switch
- return self;
- }
-
- // Select button - bring up message selection panel
-
- - select:sender
- {
- if (!selectPanel) // create new select panel if none yet
- [NXApp loadNibSection:"SelectPanel.nib" owner:self withNames:NIL];
- // attach the select panel to this window
- if (![selectPanel isVisible]) [selectPanel attachWindow:window];
- [selectPanel orderFront:self];// put up the select panel
- // make sure the date2 field is set up right
- if ([selectAfter intValue]) [self selectAfter:self];
- else {
- if ([selectBefore intValue]) [self selectBefore:self];
- else [self selectDate:self];
- }
- return self;
- }
-
-
- // Open select panel
-
- - selectHelp:sender
- {
- [NXApp selectHelp:sender];
- return self;
- }
-
-
- // Select messages on a particular date
-
- - selectDate:sender
- {
- // set new title for second date
- [selectDateText2 setTitle:""];
- // and disable the field
- [selectDateText2 setEnabled:NIL];
- // make it invisible
- [selectDate2 removeFromSuperview];
- [selectPanel display];
- return self;
- }
-
-
- // Select messages after a particular date
-
- - selectAfter:sender
- {
- // set new title for second date
- [selectDateText2 setTitle:"Before:"];
- // and enable the field
- [selectDateText2 setEnabled:T];
- // make it visible again
- [selectDate2Super addSubview:selectDate2];
- [selectPanel display];
- return self;
- }
-
-
- // Select messages before a particular date
-
- - selectBefore:sender
- {
- [self selectAfter:self]; // make it visible again
- // set proper title for second date
- [selectDateText2 setTitle:"After:"];
- [selectDate2 display]; // fix the display
- return self;
- }
-
- // Search out the messages the user wants selected
-
- - selectOK:sender
- {
- char tmp[TMPLEN*2];
- const char *s;
- int i;
- int cnt = 0;
- if (![selectRetain intValue]) // deselect all unless want retention
- for (i = 0; i < nmsgs; ++i) [browser setState:NIL at:i :0];
- strcpy (tmp,"All"); // initialize the search microprogram
- // status selection criteria
- if ([selectAnswered intValue]) strcat (tmp," Answered");
- if ([selectDeleted intValue]) strcat (tmp," Deleted");
- if ([selectFlagged intValue]) strcat (tmp," Flagged");
- if ([selectNew intValue]) strcat (tmp," New");
- if ([selectOld intValue]) strcat (tmp," Old");
- if ([selectRecent intValue]) strcat (tmp," Recent");
- if ([selectSeen intValue]) strcat (tmp," Seen");
- if ([selectUnanswered intValue]) strcat (tmp," Unanswered");
- if ([selectUndeleted intValue]) strcat (tmp," Undeleted");
- if ([selectUnflagged intValue]) strcat (tmp," Unflagged");
- if ([selectUnseen intValue]) strcat (tmp," Unseen");
- // date selection criteria
- if ((s = [selectDateText stringValue]) && strlen (s)) {
- if ([selectAfter intValue]) {
- selstr (tmp," Since",s);
- if ((s = [selectDateText2 stringValue]) && strlen (s))
- selstr (tmp," Before", s);
- }
- else {
- if ([selectBefore intValue]) {
- selstr (tmp," Before",s);
- if ((s = [selectDateText2 stringValue]) && strlen (s))
- selstr (tmp," Since", s);
- }
- else selstr (tmp," On",s);
- }
- }
- // various text-based selection criteria
- if ((s = [selectFrom stringValue]) && strlen (s)) selstr (tmp," From",s);
- if ((s = [selectTo stringValue]) && strlen (s)) selstr (tmp," To",s);
- if ((s = [selectCc stringValue]) && strlen (s)) selstr (tmp," cc",s);
- if ((s = [selectSubject stringValue]) && strlen (s))
- selstr (tmp," Subject",s);
- if ((s = [selectText stringValue]) && strlen (s)) selstr (tmp," Body",s);
- // finally append any additional criteria
- if ((s = [selectOther stringValue]) && strlen (s)) {
- strcat (tmp," "); // lead with a space
- strcat (tmp,s); // and then what user says
- }
-
- mail_search (mailStream,tmp); // do the search
- // count the number of selected messages
- for (i = 0; i < nmsgs; ++i) if ([[browser cellAt:i :0] state]) cnt++;
- switch (cnt) { // report it
- case 0: // nothing selected
- sprintf (tmp,"No messages selected");
- break;
- case 1: // let's have halfway decent English here...
- sprintf (tmp,"1 message selected");
- break;
- default:
- sprintf (tmp,"%d messages selected",cnt);
- break;
- }
- mm_log (tmp,NIL); // output the string
- return [self zoom:self]; // rezoom if necessary
- }
-
-
- // Dummy method to get out of text fields
-
- - selectNull:sender
- {
- return self;
- }
-
-
- @end
-